/*
 * Copyright (C) 2008 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2012 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WebView.h"

#include "MGWebViewInternal.h"
#include "WebViewHitTestResult.h"
#include <cassert>
#include <manx/Date.h>
#include <stdlib.h>
#include <string.h>
#include <webkit/WebCString.h>

namespace MXGlue {

using WebKit::WebString;
using WebKit::WebCString;
using WebKit::WebPoint;

// WebViewClientImpl

class WebViewClientImpl : public WebKit::WebViewClient {
public:
    WebViewClientImpl(WebView* v) : m_webView(v) { }

    virtual WebKit::WebView* createWindow(const char* url, int width, int height, bool isScript);
    virtual void closeWindowSoon();
    virtual void runJavaScriptAlert(WebString text);
    virtual bool runJavaScriptConfirm(WebString text);
    virtual bool runJavaScriptPrompt(WebString text, WebString defaultValue, WebString& result);
    virtual void didViewportChanged(float initialScale, float maximumScale, float minimumScale, bool userScalable);
    virtual void didContentSizeChanged(int width, int height);
    virtual void didUpdateDrawing(const unsigned char* buffer, int size);
    virtual void didFocusedNodeChanged();
    virtual void didCommitLoad();
    virtual void runOpenPanel();
    virtual void setCursor(int type);
    virtual void delegatedScrollRequested(WebPoint);
    virtual void setInputMethodState(bool enabled);
    virtual bool httpAuthenticationChallenge(WebString siteUrl, WebString message, WebString& user, WebString& password);
    virtual void didLocationChanged(WebString url);
    virtual void didReceiveTitle(WebString title);
    virtual bool verifyCertificate(int sslError, Manx::X509chain*);
    virtual void didLoadFailed(int error, WebString domain, WebString url, WebString localizedDescription);
    virtual bool decidePolicyForNavigationAction(WebString url, int navigationType);
    virtual void notifyUnsupportScheme(WebString url);
    virtual bool decidePolicyForNavigationWarning(int warningType);
    virtual void notifyLoadStarted(void* frame);
    virtual void notifyLoadProgress(float progress);
    virtual void notifyLoadSecurityStatus(int securityStatus);
    virtual void notifyLoadFinished();
    virtual void notifyHistoryChanged(bool canGoBack, bool canGoForward);
    virtual void startDownload(WebString url, WebString mimeType);
    virtual void didFunctionCalled(WebViewJavaScriptObjectDelegate&, int& returnType, double& returnFloat, WebString& returnString);
    virtual void didWindowObjectCleared(void* frame, void* context, void* windowObject);
    virtual void showPopupMenu(int itemCount, int selectedItem, bool multiple);
    virtual void popupMenuItem(WebString item, bool selected, bool enabled);
    virtual void hidePopupMenu();
    virtual void setCursorPosition(WebPoint);
    virtual void startGeolocationUpdating();
    virtual void stopGeolocationUpdating();
    virtual void requestGeolocationPermission(WebString origin);
    virtual void cancelGeolocationPermissionRequest(WebString origin);
    
    WebView* m_webView;
};

WebKit::WebView* WebViewClientImpl::createWindow(const char* url, int width, int height, bool isScript)
{
    WebViewManager::s_callbackIn[0].type_const_char_ptr = url;
    WebViewManager::s_callbackIn[1].type_int = width;
    WebViewManager::s_callbackIn[2].type_int = height;
    WebViewManager::s_callbackIn[3].type_bool = isScript;
    WebViewManager::processCallback(m_webView, CALLBACK_CREATE_WINDOW);
    WebView* webview = (WebView*)WebViewManager::s_callbackOut[0].type_void_ptr;
    if (!webview)
        return 0;
    return webview->m_impl;
}

void WebViewClientImpl::closeWindowSoon()
{
    WebViewManager::processCallback(m_webView, CALLBACK_CLOSE_WINDOW);
}

void WebViewClientImpl::runJavaScriptAlert(WebString text)
{
    WebCString cText = text.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = cText.isNull() ? "" : cText.data();
    WebViewManager::processCallback(m_webView, CALLBACK_JAVASCRIPT_ALERT);
}

bool WebViewClientImpl::runJavaScriptConfirm(WebString text)
{
    WebCString cText = text.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = cText.isNull() ? "" : cText.data();
    WebViewManager::processCallback(m_webView, CALLBACK_JAVASCRIPT_CONFIRM);
    return WebViewManager::s_callbackOut[0].type_bool;
}

bool WebViewClientImpl::runJavaScriptPrompt(WebString text, WebString defaultValue, WebString& result)
{
    WebCString cText = text.utf8();
    WebCString cDefaultValue = defaultValue.utf8();

    WebViewManager::s_callbackOut[0].type_bool = false;
    WebViewManager::s_callbackOut[1].type_char_ptr = new char[VARDATA_CHAR_OUT_SIZE];
    strncpy(WebViewManager::s_callbackOut[1].type_char_ptr, cDefaultValue.data(), defaultValue.length());

    WebViewManager::s_callbackIn[0].type_const_char_ptr = !cText.isNull() ? cText.data() : "";
    WebViewManager::s_callbackIn[1].type_const_char_ptr = cDefaultValue.data();
    WebViewManager::processCallback(m_webView, CALLBACK_JAVASCRIPT_PROMPT);

    result = WebString::fromUTF8(WebViewManager::s_callbackOut[1].type_char_ptr);

    delete[] WebViewManager::s_callbackOut[1].type_char_ptr;
    return WebViewManager::s_callbackOut[0].type_bool;
}

void WebViewClientImpl::didViewportChanged(float initialScale, float maximumScale, float minimumScale, bool userScalable)
{
    WebViewManager::s_callbackIn[0].type_float = initialScale;
    WebViewManager::s_callbackIn[1].type_float = maximumScale;
    WebViewManager::s_callbackIn[2].type_float = minimumScale;
    WebViewManager::s_callbackIn[3].type_bool  = userScalable;
    WebViewManager::processCallback(m_webView, CALLBACK_VIEWPORT_CHANGED);
}

void WebViewClientImpl::didContentSizeChanged(int width, int height)
{
    WebViewManager::s_callbackIn[0].type_int = width;
    WebViewManager::s_callbackIn[1].type_int = height;

    WebViewManager::processCallback(m_webView, CALLBACK_CONTENTS_SIZE_CHANGED);
}

void WebViewClientImpl::didUpdateDrawing(const unsigned char* buffer, int size)
{
    if (buffer) {
        WebViewManager::s_callbackIn[0].type_const_uchar_ptr = buffer;
        WebViewManager::s_callbackIn[1].type_int = size;
        WebViewManager::processCallback(m_webView, CALLBACK_SET_TEXTURE);
    } else
        WebViewManager::processCallback(m_webView, CALLBACK_SET_BACKINGSTORE);
}

void WebViewClientImpl::didFocusedNodeChanged()
{
    m_webView->didFocusedNodeChangedWT();
}

void WebViewClientImpl::didCommitLoad()
{
    m_webView->clearInputMethodInfoWT();
}

void WebViewClientImpl::runOpenPanel()
{
    WebViewManager::processCallback(m_webView, CALLBACK_UPLOAD_REQUEST);
}

void WebViewClientImpl::setCursor(int type)
{
    WebViewManager::s_callbackIn[0].type_int = type;
    WebViewManager::processCallback(m_webView, CALLBACK_SET_CURSOR_TYPE);
}

void WebViewClientImpl::delegatedScrollRequested(WebPoint point)
{
    WebViewManager::s_callbackIn[0].type_int = point.x;
    WebViewManager::s_callbackIn[1].type_int = point.y;
    WebViewManager::processCallback(m_webView, CALLBACK_SCROLL_REQUESTED);
}

void WebViewClientImpl::setInputMethodState(bool enabled)
{
    if (enabled)
        WebViewManager::processCallback(m_webView, CALLBACK_BEGIN_COMPOSITION);
    else
        WebViewManager::processCallback(m_webView, CALLBACK_END_COMPOSITION);
}

bool WebViewClientImpl::httpAuthenticationChallenge(WebString siteUrl, WebString message, WebString& user, WebString& password)
{
    WebCString siteUrlStr = siteUrl.utf8();
    WebCString messageStr = message.utf8();
    WebCString userStr = user.utf8();
    WebCString passwordStr = password.utf8();

    WebViewManager::s_callbackOut[0].type_bool = false;
    WebViewManager::s_callbackOut[1].type_char_ptr = new char[VARDATA_CHAR_OUT_SIZE];
    strncpy(WebViewManager::s_callbackOut[1].type_char_ptr, userStr.data(), user.length());
    WebViewManager::s_callbackOut[2].type_char_ptr = new char[VARDATA_CHAR_OUT_SIZE];
    strncpy(WebViewManager::s_callbackOut[2].type_char_ptr, passwordStr.data(), password.length());

    WebViewManager::s_callbackIn[0].type_const_char_ptr = siteUrlStr.data();
    WebViewManager::s_callbackIn[1].type_const_char_ptr = messageStr.data();
    WebViewManager::s_callbackIn[2].type_const_char_ptr = userStr.data();
    WebViewManager::s_callbackIn[3].type_const_char_ptr = passwordStr.data();
    WebViewManager::processCallback(m_webView, CALLBACK_HTTP_AUTH);

    user = WebString::fromUTF8(WebViewManager::s_callbackOut[1].type_char_ptr);
    password = WebString::fromUTF8(WebViewManager::s_callbackOut[2].type_char_ptr);

    delete[] WebViewManager::s_callbackOut[1].type_char_ptr;
    delete[] WebViewManager::s_callbackOut[2].type_char_ptr;
    return WebViewManager::s_callbackOut[0].type_bool;
}

void WebViewClientImpl::didLocationChanged(WebString url)
{
    WebCString c = url.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = c.data();
    WebViewManager::processCallback(m_webView, CALLBACK_SET_URL);
}

void WebViewClientImpl::didReceiveTitle(WebString title)
{
    WebCString c = title.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = c.data();
    WebViewManager::processCallback(m_webView, CALLBACK_SET_TITLE);
}

bool WebViewClientImpl::verifyCertificate(int sslError, Manx::X509chain* chain)
{
    char* serialbuf = 0;
    Manx::Date beforeDate;
    Manx::Date afterDate;
    char onelineissuer[1024];
    char onelinesubject[1024];

    WebViewManager::s_callbackOut[0].type_bool = false;
    WebViewManager::s_callbackIn[0].type_int = sslError;

    if (chain) {
        Manx::X509cinf* cinf = chain->getAt(0);
        if (cinf) {
            ASSERT(chain && cinf);
            Manx::X509name* x509subject = cinf->subject();
            if (x509subject)
                x509subject->oneline(onelinesubject, sizeof(onelinesubject));
            Manx::X509name* x509issuer = cinf->issuer();
            if (x509issuer)
                x509issuer->oneline(onelineissuer, sizeof(onelineissuer));

            beforeDate = cinf->notBefore();
            afterDate = cinf->notAfter();

            cinf->serialNumberStr(&serialbuf);
        }
    }

    WebViewManager::s_callbackIn[1].type_const_char_ptr = onelinesubject;
    WebViewManager::s_callbackIn[2].type_const_char_ptr = onelineissuer;
    WebViewManager::s_callbackIn[3].type_float = (float)(Manx::X509cinf::convertTime64_t(&beforeDate));
    WebViewManager::s_callbackIn[4].type_float = (float)(Manx::X509cinf::convertTime64_t(&afterDate));
    WebViewManager::s_callbackIn[5].type_const_char_ptr = serialbuf;
    WebViewManager::processCallback(m_webView, CALLBACK_CERTIFICATE_VERIFY);

    if (serialbuf)
        delete [] serialbuf;

    return WebViewManager::s_callbackOut[0].type_bool;
}

void WebViewClientImpl::didLoadFailed(int error, WebString domain, WebString url, WebString localizedDescription)
{
    WebCString cDomain = domain.utf8();
    WebCString cURL = url.utf8();
    WebCString cLocalizedDescription = localizedDescription.utf8();

    WebViewManager::s_callbackIn[0].type_int = error;
    WebViewManager::s_callbackIn[1].type_const_char_ptr = cDomain.data();
    WebViewManager::s_callbackIn[2].type_const_char_ptr = cURL.data();
    WebViewManager::s_callbackIn[3].type_const_char_ptr = cLocalizedDescription.data();
    WebViewManager::processCallback(m_webView, CALLBACK_LOAD_FAILED);
}

bool WebViewClientImpl::decidePolicyForNavigationAction(WebString url, int navigationType)
{
    WebCString requestUrl = url.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = !requestUrl.isNull() ? requestUrl.data() : "";
    WebViewManager::s_callbackIn[1].type_int = navigationType;
    // set dafault value.
    WebViewManager::s_callbackOut[0].type_bool = true;
    WebViewManager::processCallback(m_webView, CALLBACK_NAVIGATION_CONFIRM);
    return WebViewManager::s_callbackOut[0].type_bool;
}

void WebViewClientImpl::notifyUnsupportScheme(WebString url)
{
    WebCString c = url.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = c.data();
    WebViewManager::processCallback(m_webView, CALLBACK_UNSUPPORT_SCHEME);
}

bool WebViewClientImpl::decidePolicyForNavigationWarning(int warningType)
{
    WebViewManager::s_callbackIn[0].type_int = warningType;
    WebViewManager::s_callbackOut[0].type_bool = false;
    WebViewManager::processCallback(m_webView, CALLBACK_NAVIGATION_WARNING);
    return WebViewManager::s_callbackOut[0].type_bool;
}

void WebViewClientImpl::notifyLoadStarted(void* frame)
{
    WebViewManager::s_callbackIn[0].type_void_ptr = frame;
    WebViewManager::processCallback(m_webView, CALLBACK_LOAD_STARTED);

    WebViewManager::s_callbackIn[0].type_float = 0;
    WebViewManager::processCallback(m_webView, CALLBACK_LOAD_PROGRESS_CHANGED);
}

void WebViewClientImpl::notifyLoadProgress(float progress)
{
    WebViewManager::s_callbackIn[0].type_float = progress;
    WebViewManager::processCallback(m_webView, CALLBACK_LOAD_PROGRESS_CHANGED);
}

void WebViewClientImpl::notifyLoadSecurityStatus(int securityStatus)
{
    WebViewManager::s_callbackIn[0].type_int = securityStatus;
    WebViewManager::processCallback(m_webView, CALLBACK_LOAD_SECURE);
}

void WebViewClientImpl::notifyLoadFinished()
{
    WebViewManager::processCallback(m_webView, CALLBACK_LOAD_FINISHED);
}

void WebViewClientImpl::notifyHistoryChanged(bool canGoBack, bool canGoForward)
{
    WebViewManager::s_callbackIn[0].type_bool = canGoBack;
    WebViewManager::s_callbackIn[1].type_bool = canGoForward;
    WebViewManager::processCallback(m_webView, CALLBACK_HISTORY_CHANGED);
}

void WebViewClientImpl::startDownload(WebString url, WebString mimeType)
{
    WebCString cURL = url.utf8();
    WebCString cMimeType = mimeType.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = cURL.data();
    WebViewManager::s_callbackIn[1].type_const_char_ptr = cMimeType.data();
    WebViewManager::processCallback(m_webView, CALLBACK_DOWNLOAD_START);
}

void WebViewClientImpl::didFunctionCalled(WebViewJavaScriptObjectDelegate& delegate, int& returnType, double& value, WebString& string)
{
    WebViewManager::s_callbackOut[0].type_int = returnType;
    WebViewManager::s_callbackOut[1].type_int = 0;
    WebViewManager::s_callbackOut[2].type_char_ptr = new char[VARDATA_CHAR_OUT_SIZE];
    memset(WebViewManager::s_callbackOut[2].type_char_ptr, 0, VARDATA_CHAR_OUT_SIZE);
    WebViewManager::s_callbackIn[0].type_void_ptr = static_cast<void*>(&delegate);

    WebViewManager::processCallback(m_webView, CALLBACK_JAVASCRIPT_DELEGATE);

    returnType = WebViewManager::s_callbackOut[0].type_int;
    value = WebViewManager::s_callbackOut[1].type_int;
    string = WebString::fromUTF8(WebViewManager::s_callbackOut[2].type_char_ptr);
    delete[] WebViewManager::s_callbackOut[2].type_char_ptr;
}

void WebViewClientImpl::didWindowObjectCleared(void* frame, void* context, void* windowObject)
{
    WebViewManager::s_callbackIn[0].type_void_ptr = 0;
    WebViewManager::s_callbackIn[1].type_void_ptr = frame;
    WebViewManager::s_callbackIn[2].type_void_ptr = context;
    WebViewManager::s_callbackIn[3].type_void_ptr = windowObject;
    WebViewManager::processCallback(m_webView, CALLBACK_WINDOW_OBJECT_CLEARED);
}


void WebViewClientImpl::showPopupMenu(int itemCount, int selectedItem, bool multiple)
{
    WebViewManager::s_callbackIn[0].type_int = itemCount;
    WebViewManager::s_callbackIn[1].type_int = selectedItem;
    WebViewManager::s_callbackIn[2].type_bool= multiple;
    WebViewManager::processCallback(m_webView, CALLBACK_SHOW_POPUP_MENU);
}

void WebViewClientImpl::popupMenuItem(WebString string, bool selected, bool enabled)
{
    WebCString c = string.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = c.data();
    WebViewManager::s_callbackIn[1].type_bool = selected;
    WebViewManager::s_callbackIn[2].type_bool = enabled;
    WebViewManager::processCallback(m_webView, CALLBACK_SHOW_POPUP_MENU_ITEM);
}

void WebViewClientImpl::hidePopupMenu()
{
    WebViewManager::processCallback(m_webView, CALLBACK_HIDE_POPUP_MENU);
}

void WebViewClientImpl::setCursorPosition(WebPoint point)
{
    WebViewManager::s_callbackIn[0].type_int = point.x;
    WebViewManager::s_callbackIn[1].type_int = point.y;
    WebViewManager::processCallback(m_webView, CALLBACK_SET_CURSOR_POSITION);
}

void WebViewClientImpl::startGeolocationUpdating()
{
    WebViewManager::processCallback(m_webView, CALLBACK_START_GEOLOCATION_UPDATING);
}

void WebViewClientImpl::stopGeolocationUpdating()
{
    WebViewManager::processCallback(m_webView, CALLBACK_STOP_GEOLOCATION_UPDATING);
}

void WebViewClientImpl::requestGeolocationPermission(WebString origin)
{
    WebCString c = origin.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = c.data();
    WebViewManager::processCallback(m_webView, CALLBACK_REQUEST_GEOLOCATION_PERMISSION);
}

void WebViewClientImpl::cancelGeolocationPermissionRequest(WebString origin)
{
    WebCString c = origin.utf8();
    WebViewManager::s_callbackIn[0].type_const_char_ptr = c.data();
    WebViewManager::processCallback(m_webView, CALLBACK_CANCEL_GEOLOCATION_PERMISSION_REQUEST);
}

/* class WebView */
bool WebView::isWebThread()
{
    return WebViewManager::isWebThread();
}

WebView::WebView(WebKit::WebViewCreateParam& param)
{
    assert(isWebThread());

    m_webViewClient = new WebViewClientImpl(this);
    m_impl = WebKit::WebView::create(*m_webViewClient);
    ASSERT(m_impl);
    m_impl->create(param);
    clearInputMethodInfoWT();
}

WebView::~WebView()
{
    assert(isWebThread());

    delete m_impl;
    delete m_webViewClient;
}

void WebView::drawRect(float x, float y, float w, float h, void* data, unsigned dataSize)
{
    m_impl->drawRect(x, y, w, h, data, dataSize);
}

void WebView::setClipRect(int x, int y, int w, int h, bool pending)
{
    {
        if (m_impl->updateClipRect(x, y, w, h, pending)) {
            WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_CLIP_RECT);
            WebViewManager::pushCommand(cmd);
        }
    }
}

void WebView::sendScrollEvent(ScrollEventType event, int x, int y)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SCROLL_EVENT);
        cmd->m_data[0].typeScrollEvent = event;
        cmd->m_data[1].typeInt = x;
        cmd->m_data[2].typeInt = y;
        WebViewManager::pushCommand(cmd, 1);
    }
}

void WebView::sendMouseEvent(MouseEventType event, MouseButtonType button, int x, int y)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, (event == MOUSE_EVENT_MOVED) ? WEBVIEW_COMMAND_MOUSE_EVENT_MOVE : WEBVIEW_COMMAND_MOUSE_EVENT);
        cmd->m_data[0].typeMouseEvent = event;
        cmd->m_data[1].typeMouseButton = button;
        cmd->m_data[2].typeInt = x;
        cmd->m_data[3].typeInt = y;
        WebViewManager::pushCommand(cmd, (event == MOUSE_EVENT_MOVED) ? 10 : 0);
    }
}

void WebView::sendKeyEvent(KeyEventType event, KeyType key, char ascii, bool shift, bool ctrl, bool alt, bool win)
{
    sendKeyEvent2(event, key, ascii, shift, ctrl, alt, win, KeyDefaultBehaviorCursorNavigation);
}

void WebView::sendKeyEvent2(KeyEventType event, KeyType key, char ascii, bool shift, bool ctrl, bool alt, bool win, KeyDefaultBehavior behavior)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_KEY_EVENT);
        cmd->m_data[0].typeKeyEvent = event;
        cmd->m_data[1].typeKey = key;
        cmd->m_data[2].typeChar = ascii;
        cmd->m_data[3].typeBool = shift;
        cmd->m_data[4].typeBool = ctrl;
        cmd->m_data[5].typeBool = alt;
        cmd->m_data[6].typeBool = win;
        cmd->m_data[7].typeInt = behavior;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::sendHtmlEvent(HtmlEventType event, const HtmlEventData* data)
{
    {
        int size;
        switch (event) {
        case HTML_INPUT_ELEMENT_SET_VALUE:  size = 1; break;
        default:                            size = 0; break;
        }

        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_HTML_EVENT);
        cmd->m_data[0].typeHtmlEvent = event;
        cmd->m_data[1].typeHtmlEventDataPtr = 0;
        if (size) {
            cmd->m_data[1].typeHtmlEventDataPtr = new HtmlEventData[size];
            memcpy(cmd->m_data[1].typeHtmlEventDataPtr, data, size * sizeof(HtmlEventData));
        }
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::sendTouchEvent(const Manx::TouchEvent* event)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_TOUCH_EVENT);
        cmd->m_data[0].typeTouchEventDataPtr = new Manx::TouchEvent();
        memcpy(cmd->m_data[0].typeTouchEventDataPtr, event, sizeof(Manx::TouchEvent));
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::sendGestureEvent(unsigned type, int x, int y)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_GESTURE_EVENT);
        cmd->m_data[0].typeChar = type;
        cmd->m_data[0].typeInt = x;
        cmd->m_data[1].typeInt = y;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::loadUrl(const char* url)
{
    if (!url)
        return;

    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_LOAD_URL);
        cmd->m_data[0].typeCharPtr = new char[strlen(url) + 1];
        strncpy(cmd->m_data[0].typeCharPtr, url, strlen(url) + 1);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::loadHtml(const char *html, const char *baseUrl)
{
    if (!html || !baseUrl)
        return;

    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_LOAD_HTML);
        cmd->m_data[0].typeCharPtr = new char[strlen(html) + 1];
        strncpy(cmd->m_data[0].typeCharPtr, html, strlen(html) + 1);
        cmd->m_data[1].typeCharPtr = new char[strlen(baseUrl) + 1];
        strncpy(cmd->m_data[1].typeCharPtr, baseUrl, strlen(baseUrl) + 1);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::loadData(const char *data, unsigned long dataLen, const char *mimetype, const char *encoding, const char *baseUrl)
{
    if (!data || !mimetype || !encoding || !baseUrl)
        return;

    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_LOAD_DATA);
        cmd->m_data[0].typeCharPtr = new char[dataLen + 1];
        strncpy(cmd->m_data[0].typeCharPtr, data, dataLen + 1);
        cmd->m_data[1].typeLong = dataLen;
        cmd->m_data[2].typeCharPtr = new char[strlen(mimetype) + 1];
        strncpy(cmd->m_data[2].typeCharPtr, mimetype, strlen(mimetype) + 1);
        cmd->m_data[3].typeCharPtr = new char[strlen(encoding) + 1];
        strncpy(cmd->m_data[3].typeCharPtr, encoding, strlen(encoding) + 1);
        cmd->m_data[4].typeCharPtr = new char[strlen(baseUrl) + 1];
        strncpy(cmd->m_data[4].typeCharPtr, baseUrl, strlen(baseUrl) + 1);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::loadArchive(const char *buf, unsigned long bufLen, const char *url)
{
    if (!url)
        return;

    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_LOAD_ARCHIVE);
        cmd->m_data[0].typeCharPtr = new char[bufLen + 1];
        strncpy(cmd->m_data[0].typeCharPtr, buf, bufLen + 1);
        cmd->m_data[1].typeLong = bufLen;
        cmd->m_data[2].typeCharPtr = new char[strlen(url) + 1];
        strncpy(cmd->m_data[2].typeCharPtr, url, strlen(url) + 1);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::reload()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_RELOAD);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::stop()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_STOP);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::goBack()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_GO_BACK);
        WebViewManager::pushCommand(cmd, 1);
    }
}

void WebView::goForward()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_GO_FORWARD);
        WebViewManager::pushCommand(cmd, 1);
    }
}

void WebView::increaseTextSize()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_INCREASE_TEXT_SIZE);
        WebViewManager::pushCommand(cmd, 1);
    }
}

void WebView::decreaseTextSize()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_DECREASE_TEXT_SIZE);
        WebViewManager::pushCommand(cmd, 1);
    }
}

void WebView::resetTextSize()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_RESET_TEXT_SIZE);
        WebViewManager::pushCommand(cmd, 1);
    }
}

void WebView::setCaretVisible(bool visible)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_CARET_VISIBLE);
        cmd->m_data[0].typeBool = visible;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::setComposition(const char *compositionText, unsigned underlineFrom, unsigned underlineTo)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_COMPOSITION);
        cmd->m_data[0].typeCharPtr = new char[strlen(compositionText) + 1];
        strncpy(cmd->m_data[0].typeCharPtr, compositionText, strlen(compositionText) + 1);
        cmd->m_data[1].typeInt = underlineFrom;
        cmd->m_data[2].typeInt = underlineTo;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::confirmComposition(const char *compositionText, unsigned caretIndex)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_CONFIRM_COMPOSITION);
        cmd->m_data[0].typeCharPtr = new char[strlen(compositionText) + 1];
        strncpy(cmd->m_data[0].typeCharPtr, compositionText, strlen(compositionText) + 1);
        cmd->m_data[1].typeInt = caretIndex;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::cancelComposition()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_CANCEL_COMPOSITION);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::exitComposition()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_EXIT_COMPOSITION);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::clearText()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_CLEAR_TEXT);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::setSelectedItemsPopupMenu(int index, bool isEnd)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SELECT_POPUPMENU);
        cmd->m_data[0].typeInt = index;
        cmd->m_data[1].typeBool = isEnd;

        WebViewManager::pushCommand(cmd);
    }
}

void WebView::getItemPopupMenu(int index)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_GETITEM_POPUPMENU);
        cmd->m_data[0].typeInt = index;

        WebViewManager::pushCommand(cmd);
    }
}

void WebView::setWebViewSettingsScriptEnabled(bool enable)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_SETTINGS_JS);
        cmd->m_data[0].typeBool = enable;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::setWebGLEnabled(bool enabled)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_SETTINGS_WEBGL);
        cmd->m_data[0].typeBool = enabled;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::setUserAgent(const char* userAgent)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_USERAGENT);
        cmd->m_data[0].typeCharPtr = new char[strlen(userAgent) + 1];
        strncpy(cmd->m_data[0].typeCharPtr, userAgent, strlen(userAgent) + 1);
        WebViewManager::pushCommand(cmd);
    }
}

const char* WebView::userAgent()
{
    return m_impl->userAgent();
}

void WebView::setJavaScriptObj(WebViewJavaScriptObject* window)
{
    WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_JAVA_SCRIPT_OBJ);
    cmd->m_data[0].typeVoidPtr = window;
    WebViewManager::pushCommand(cmd);
}

void WebView::setWebViewSettingsCookieEnabled(bool enable)
{
    WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_SETTINGS_COOKIE);
    cmd->m_data[0].typeBool = enable;
    WebViewManager::pushCommand(cmd);
}

void WebView::deleteAllCookies()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_DELETE_ALL_COOKIES);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::hitTest(Manx::Point& hitPoint, int type)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_HITTEST);
        cmd->m_data[0].typeInt = hitPoint.x;
        cmd->m_data[1].typeInt = hitPoint.y;
        cmd->m_data[2].typeInt = type;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::imageBuffer()
{
}

void WebView::setUploadFile(const char* path, const char* name)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_UPLOAD);
        cmd->m_data[0].typeCharPtr = new char[strlen(path) + 1];
        strncpy(cmd->m_data[0].typeCharPtr, path, strlen(path) + 1);
        cmd->m_data[1].typeCharPtr = new char[strlen(name) + 1];
        strncpy(cmd->m_data[1].typeCharPtr, name, strlen(name) + 1);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::setCookies(const char* url, const char* cookie)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_COOKIES);
        cmd->m_data[0].typeCharPtr = new char[strlen(url) + 1];
        strncpy(cmd->m_data[0].typeCharPtr, url, strlen(url) + 1);
        cmd->m_data[1].typeCharPtr = new char[strlen(cookie) + 1];
        strncpy(cmd->m_data[1].typeCharPtr, cookie, strlen(cookie) + 1);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::setWebSecurityEnabled(bool enabled)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_SETTINGS_WEBSECURITY);
        cmd->m_data[0].typeBool = enabled;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::setDeveloperExtrasEnabled(bool enabled)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_SETTINGS_DEVELOPEREXTRAS);
        cmd->m_data[0].typeBool = enabled;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::releaseTiles()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_RELEASE_TILES);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::overflowScroll(Manx::Point &hitPoint, int deltaX, int deltaY)
{
    m_impl->overflowScroll(deltaX, deltaY);
}

void WebView::setDefaultLayoutWidth(int width)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_LAYOUT_WIDTH);
        cmd->m_data[0].typeInt = width;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::requestDownloadImage()
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_REQUEST_DL_IMAGE);
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::requestDownloadImageData(int pos, int size)
{
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_REQUEST_DL_IMAGE_DATA);
        cmd->m_data[0].typeInt = pos;
        cmd->m_data[1].typeInt = size;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::setGeolocationPermission(const char* origin, bool isAllowed)
{
    if (!origin)
        return;
    {
        WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_SET_GEOLOCATION_PERMISSION);
        cmd->m_data[0].typeCharPtr = new char[strlen(origin) + 1];
        strncpy(cmd->m_data[0].typeCharPtr, origin, strlen(origin) + 1);
        cmd->m_data[1].typeBool = isAllowed;
        WebViewManager::pushCommand(cmd);
    }
}

void WebView::updateGeolocationPosition(const WebKit::WebViewGeolocationPosition* position)
{
    WebViewCommand* cmd = new WebViewCommand(this, WEBVIEW_COMMAND_UPDATE_GEOLOCATION);
    cmd->m_data[0].typeVoidPtr = (void*)position;
    WebViewManager::pushCommand(cmd);
}

void WebView::didFocusedNodeChangedWT()
{
    using namespace WebKit;

    assert(isWebThread());

    m_impl->getInputMethodInfo(m_inputMethodInfo);
    if (m_impl->isProcessingUserGesture())
        return;
    bool shouldUseInputMethod = m_inputMethodInfo.type != FocusedNodeUnknown;
    if (shouldUseInputMethod)
        m_impl->setCaretVisible(false);
    else
        didInputTextBluredWT();
}

void WebView::checkInputMethodStateWT()
{
    using namespace WebKit;

    assert(isWebThread());

    InputMethodInfo& info = m_inputMethodInfo;
    bool shouldUseInputMethod = info.type != FocusedNodeUnknown;
    if (shouldUseInputMethod) {
        WebCString title = info.title.utf8();
        WebCString value = info.value.utf8();
        const char* type = "";
        switch (info.type) {
        case FocusedNodeText:
            type = "text";
            break;
        case FocusedNodePassword:
            type = "password";
            break;
        case FocusedNodeNumber:
            type = "number";
            break;
        case FocusedNodeTel:
            type = "tel";
            break;
        case FocusedNodeEMail:
            type = "email";
            break;
        case FocusedNodeURL:
            type = "url";
            break;
        case FocusedNodeSearch:
            type = "search";
            break;
        }
        WebViewManager::s_callbackIn[0].type_bool = true;
        WebViewManager::s_callbackIn[1].type_const_char_ptr = !title.isNull() ? title.data() : "";
        WebViewManager::s_callbackIn[2].type_const_char_ptr = !value.isNull() ? value.data() : "";
        WebViewManager::s_callbackIn[3].type_const_char_ptr = type;
        WebViewManager::s_callbackIn[4].type_int = info.rect.x;
        WebViewManager::s_callbackIn[5].type_int = info.rect.y;
        WebViewManager::s_callbackIn[6].type_int = info.rect.width;
        WebViewManager::s_callbackIn[7].type_int = info.rect.height;
        WebViewManager::s_callbackIn[8].type_bool = info.isMultiple;
        WebViewManager::processCallback(this, CALLBACK_FOCUS_INPUT_TEXT);

        m_impl->setCaretVisible(true);
    } else
        didInputTextBluredWT();
    
}

void WebView::didInputTextBluredWT()
{
    assert(isWebThread());

    WebViewManager::s_callbackIn[0].type_bool = false;
    WebViewManager::processCallback(this, CALLBACK_FOCUS_INPUT_TEXT);
}

void WebView::clearInputMethodInfoWT()
{
    using namespace WebKit;

    assert(isWebThread());

    m_inputMethodInfo.type = FocusedNodeUnknown;
}

void WebView::requestDownloadImageWT()
{
    assert(isWebThread());

    WebString url;
    const char* data = 0;
    unsigned imageSize = 0;
    bool ok = m_impl->getImageInfoHit(url, data, imageSize);

    if (ok) {
        WebCString c = url.utf8();
        WebViewManager::s_callbackIn[0].type_const_char_ptr = c.data();
        WebViewManager::s_callbackIn[1].type_int = data ? imageSize : 0;
        WebViewManager::processCallback(this, CALLBACK_DL_IMAGE_START);
    } else
        WebViewManager::processCallback(this, CALLBACK_DL_IMAGE_FAILED);
}

void WebView::requestDownloadImageDataWT(int pos, int requestSize)
{
    assert(isWebThread());

    WebString url;
    const char* data = 0;
    unsigned imageSize = 0;
    bool ok = m_impl->getImageInfoHit(url, data, imageSize);

    if (!ok)
        return;
    
    bool success = false;
    bool completed = false;
    if (pos < imageSize && data) {
        int length = (imageSize - pos) < requestSize ? (imageSize - pos) : requestSize;
        WebViewManager::s_callbackIn[0].type_const_char_ptr = (data + pos);
        WebViewManager::s_callbackIn[1].type_int = length;
        WebViewManager::processCallback(this, CALLBACK_DL_IMAGE_PROGRESS);
        success = true;
        completed = ((pos + length) == imageSize);
    }

    if (completed)
        WebViewManager::processCallback(this, CALLBACK_DL_IMAGE_FINISHED);
    else if (!success)
        WebViewManager::processCallback(this, CALLBACK_DL_IMAGE_FAILED);
}

} // namespace MXGlue
